#import <objc/HashTable.h>
#import <objc/Storage.h>
#import <strings.h>

#import "ObserveDispatch.h"

@implementation ObserveDispatch

- init
{
    [super init];
	
    // Create the observers list
    // The key will be the name of the method
    observerTable = [[HashTable alloc] initKeyDesc:"*" valueDesc:"@"];

    return self;
}

/*
**  Method: initObserver:
**
**  Purpose:  Initialize an observer record that can then be added to the
**		List of observers for an action.  This is a good method for sub-classes
**		to overwrite if they want to accept shorthand action names and turn 
**		them into something more appropriate.
*/

- initObserver:(observerrec *)anObserver withObject:(id)anObject
    forAction:(char *)anAction withFilter:(char *)aFilter
{
    anObserver->active = YES;
    anObserver->anObject = anObject;
    anObserver->action = NXCopyStringBuffer(anAction);
    anObserver->filter = NXCopyStringBuffer(aFilter);

    return self;
}

/*
	Method: observerListForAction:
		Returns a list of the observers that are watching for the given
		action.

	Return Value:
		Returns the list of observers if there is one or nil otherwise
*/

- observerListForAction:(char *)anAction
{
    id aList = nil;

    aList = (id)[observerTable valueForKey:anAction];
    
	return aList;
}

/*
	Method: addObserver:forAction:withFilter:
		This method is the primary interface for adding an observer to 
		a watch list for a given action.
	
	Parameters:
		id anObject		The object that is the observer
		char *actionList	A comma separated list of the actions the observer 
							wants to watch for.
		char *aFilter	additional information (unused)

    Return Value:
		Always returns 0 
*/

- (int)addObserver:(id)anObject 
	forAction:(char *)actionList 
	withFilter:(char *)aFilter
{
    id aList = nil;
    observerrec newobserver;
	    
    #if DBG_ADDSERVER
    printf("ObserveD - addObserver:forAction: %s\n",actionList);
    #endif DBG_ADDSERVER

    if (!actionList || !*actionList)
	    return -1;
		
    // Create an observer record
    [self initObserver:&newobserver withObject:anObject
	    forAction:actionList withFilter:aFilter];
				
	// Add it to the hash table
    // Look for the list this belongs to
    aList = [self observerListForAction:newobserver.action];
			
    // If not found create it
    if (!aList)
    {
        aList = [[Storage allocFromZone:[self zone]]
			initCount:0
            elementSize:sizeof(observerrec)
            description:"{i@**}"];
		
		// Add the new list to the table of lists
        [observerTable insertKey:NXCopyStringBuffer(newobserver.action) 
            value:aList];
	}
			
    // Add the observer to the list
    [aList addElement:&newobserver];
	
    return NO;
}

/*
	Method: addObserver:forAction:
		Same as addObserver:forAction:withFilter:, but passes a blank filter
		by default.
	
	Return Value:
		returns whatever addObserver:forAction:withFilter: returns
*/
- (int)addObserver:(id)anObject 
	forAction:(char *)actionList 
{
    return [self addObserver:anObject forAction:actionList withFilter:""];
}

/*
	Method: forward::
		
	Return Value:
		always returns self
*/

- forward:(SEL)aSelector :(marg_list)argFrame
{
    id aList = nil;
    char *selectorName = sel_getName(aSelector);
	
    // Find the list of observers for this method
	// Send the message to each member of the list;

    // Dispatch the message to all observers
	aList = [observerTable valueForKey:selectorName];
	if (aList)
	{
        observerrec *anObserver;
        int numObservers = [aList count];
		int counter = 0;
		
	    // Now we have the list of observers
		// For each one of them
		for (counter = 0; counter < numObservers; counter++)
		{
		    // send out the same message
		    anObserver = [aList elementAt:counter];
			[anObserver->anObject performv:aSelector :argFrame];
		}
	}

    return self;
}


@end